Child and Dependent Care Credit#

The Child and Dependent Care Tax Credit (CDCC) provides a share of care expenses depending on income.

Examples#

Consider a single parent with $8,000 child care expenses per child (the CDCC’s maximum in 2021, up to two children). Since the CDCC is capped at the lower of head and spouse’s earnings, this example is limited to single parents. In 2020, the CDCC phased out only at one level; in 2021 only, it phased out twice, and in 2022 and beyond, it returns to 2020 law.

from policyengine_us import IndividualSim
import pandas as pd
import plotly.express as px


def make_cdcc(children, year):
    sim = IndividualSim(year=year)
    sim.add_person(name="head", is_tax_unit_head=True)
    members = ["head"]
    for i in range(children):
        child = "child{}".format(i)
        sim.add_person(name=child, age=5)
        members += [child]
    sim.add_tax_unit(
        name="tax_unit",
        members=members,
        tax_unit_childcare_expenses=8_000 * children,
    )
    sim.add_spm_unit(name="spm_unit", members=members)
    sim.add_household(name="household", members=members, state_code="MD")
    sim.vary("employment_income", max=500_000)
    return pd.DataFrame(
        dict(
            employment_income=sim.calc("employment_income")[0],
            cdcc=sim.calc("cdcc")[0].round(),
            cdcc_mtr=-sim.deriv(
                "cdcc",
                "employment_income",
                wrt_target="head",
            ),
            children=children,
            year=year,
        )
    )


# Make a table of CDCC amounts for different numbers of adults and children.
l = []
for year in [2020, 2021, 2022]:
    for children in range(1, 3):
        l.append(make_cdcc(children, year))

df = pd.concat(l)

LABELS = dict(
    employment_income="Employment income",
    cdcc="CDCC",
    cdcc_mtr="CDCC Marginal Tax Rate",
    children="Children",
    year="Year",
)

fig = px.line(
    df,
    "employment_income",
    "cdcc",
    color="children",
    animation_frame="year",
    labels=LABELS,
    title="Child and Dependent Care Credit",
)
fig.update_layout(
    xaxis_tickformat="$,",
    yaxis_tickformat="$,",
    yaxis_range=[0, df.cdcc.max() * 1.05],
)
fig.show()

The credit phases in at 50% of income and then phases out in a stepwise fashion that produces many small cliffs (infinite marginal tax rates).

fig = px.line(
    df,
    "employment_income",
    "cdcc_mtr",
    color="children",
    animation_frame="year",
    labels=LABELS,
    title="CDCC marginal tax rate",
)
fig.update_layout(
    xaxis_tickformat="$,", yaxis_tickformat=".1%", yaxis_range=[-1, 1]
)
fig.show()

Budgetary impact#

Applying 2022 rules to the 2020 Current Population Survey, OpenFisca US estimates that the CDCC pays out $4.0 billion.

from policyengine_us import Microsimulation

sim = Microsimulation(dataset_year=2020)

sim.calc("cdcc", period=2022).sum() / 1e6
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:182, in Simulation.build_from_dataset(self)
    181 try:
--> 182     data = self.dataset.load(self.dataset_year)
    183 except:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/data/dataset.py:126, in Dataset.load(self, year, key, mode)
    124 if key is None:
    125     # If no key provided, return the basic H5 reader.
--> 126     return h5py.File(file, mode=mode)
    127 else:
    128     # If key provided, return only the values requested.

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/h5py/_hl/files.py:567, in File.__init__(self, name, mode, driver, libver, userblock_size, swmr, rdcc_nslots, rdcc_nbytes, rdcc_w0, track_order, fs_strategy, fs_persist, fs_threshold, fs_page_size, page_buf_size, min_meta_keep, min_raw_keep, locking, alignment_threshold, alignment_interval, meta_block_size, **kwds)
    564     fcpl = make_fcpl(track_order=track_order, fs_strategy=fs_strategy,
    565                      fs_persist=fs_persist, fs_threshold=fs_threshold,
    566                      fs_page_size=fs_page_size)
--> 567     fid = make_fid(name, mode, userblock_size, fapl, fcpl, swmr=swmr)
    569 if isinstance(libver, tuple):

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/h5py/_hl/files.py:231, in make_fid(name, mode, userblock_size, fapl, fcpl, swmr)
    230         flags |= h5f.ACC_SWMR_READ
--> 231     fid = h5f.open(name, flags, fapl=fapl)
    232 elif mode == 'r+':

File h5py/_objects.pyx:54, in h5py._objects.with_phil.wrapper()

File h5py/_objects.pyx:55, in h5py._objects.with_phil.wrapper()

File h5py/h5f.pyx:106, in h5py.h5f.open()

FileNotFoundError: [Errno 2] Unable to open file (unable to open file: name = '/home/runner/work/policyengine-us/policyengine-us/policyengine_us/data/storage/cps_2020.h5', errno = 2, error message = 'No such file or directory', flags = 0, o_flags = 0)

During handling of the above exception, another exception occurred:

TypeError                                 Traceback (most recent call last)
Cell In[3], line 3
      1 from policyengine_us import Microsimulation
----> 3 sim = Microsimulation(dataset_year=2020)
      5 sim.calc("cdcc", period=2022).sum() / 1e6

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:131, in Simulation.__init__(self, tax_benefit_system, populations, situation, dataset, dataset_year, reform)
    129     self.dataset = dataset
    130     self.dataset_year = dataset_year
--> 131     self.build_from_dataset()
    133 # Backwards compatibility methods
    134 self.calc = self.calculate

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:184, in Simulation.build_from_dataset(self)
    182         data = self.dataset.load(self.dataset_year)
    183     except:
--> 184         data = self.dataset.load()
    185 except FileNotFoundError as e:
    186     raise FileNotFoundError(
    187         f"The dataset file {self.dataset.name} (with year {self.dataset_year}) could not be found. "
    188         + "Make sure you have downloaded or built it using the `policyengine-core data` command."
    189     ) from e

TypeError: load() missing 1 required positional argument: 'year'

However, since it interacts with other programs, we need to compare total income after repealing the program to estimate the true budgetary impact. Applying this method yields a lower $3.8 billion.

from policyengine_us.model_api import *


class ignore_reported(Reform):
    def apply(self):
        self.neutralize_variable("spm_unit_net_income_reported")


class neutralize_cdcc(Reform):
    def apply(self):
        self.neutralize_variable("cdcc")


sim = Microsimulation(ignore_reported, year=2020)
sim_no_cdcc = Microsimulation((ignore_reported, neutralize_cdcc), year=2020)

(
    sim.calc("spm_unit_net_income", period=2022).sum()
    - sim_no_cdcc.calc("spm_unit_net_income", period=2022).sum()
) / 1e6
3769.459399326172